use std::sync::Arc;

use ane_system::dao::resource::ResourceDo;
use axum::{
    extract::{Path, Query},
    response::IntoResponse,
    routing::{get, put},
    Extension, Router,
};
use casbin::MgmtApi;
use common::page::{PageInfo, PageParams};
use sea_orm::TransactionTrait;
use validator::Validate;

use crate::{
    error::{AppError, AppResult},
    extract::{Json, ValJson},
    AppState,
};

pub fn get_route<S>() -> Router<S>
where
    S: Sync + Send + Clone + 'static,
{
    Router::<S>::new()
        .route("/", get(fetch_list).post(create))
        .route("/all", get(fetch_all))
        .route("/:id", put(update).delete(delete))
}

pub async fn fetch_list(
    Extension(state): Extension<Arc<AppState>>,
    Query(page_params): Query<PageParams>,
) -> AppResult<impl IntoResponse> {
    ane_system::dao::Resource::list(state.db(), page_params.zero_page(), page_params.page_size)
        .await
        .map(|(total, datas)| PageInfo::new(datas, &page_params, total))
        .map(Json)
        .map_err(Into::into)
}

pub async fn fetch_all(Extension(state): Extension<Arc<AppState>>) -> AppResult<impl IntoResponse> {
    ane_system::dao::Resource::all(state.db())
        .await
        .map(Json)
        .map_err(Into::into)
}

pub async fn create(
    Extension(state): Extension<Arc<AppState>>,
    Json(data): Json<ResourceDo>,
) -> AppResult<impl IntoResponse> {
    data.validate()?;
    ane_system::dao::Resource::create(state.db(), data)
        .await
        .map(Json)
        .map_err(Into::into)
}

pub async fn update(
    Extension(state): Extension<Arc<AppState>>,
    Path(id): Path<i32>,
    ValJson(data): ValJson<ResourceDo>,
) -> AppResult<impl IntoResponse> {
    let resource = ane_system::dao::Resource::fetch_one(state.db(), id)
        .await?
        .ok_or(AppError::NotFount)?;
    let is_status_change = resource.status != data.status;
    let is_changed =
        is_status_change || resource.path != data.path || resource.method != data.method;
    let new_path = data.path.clone();
    let new_method = data.method.clone();
    let new_status = data.status;
    let res = ane_system::dao::Resource::update(state.db(), id, data)
        .await
        .map(Json)
        .map_err(Into::into);
    if is_changed {
        let enforcer = state.enforcer();
        if !is_status_change {
            // 数据改变了, status没有变并且status是启用的
            if resource.status {
                let mut enforcer = enforcer.lock().await;
                let path = resource.path;
                let method = resource.method;
                let policies = enforcer
                    .get_filtered_policy(1, vec![path.clone(), method.clone()])
                    .into_iter()
                    .map(|f| {
                        vec![
                            f.into_iter().next().unwrap(),
                            new_path.clone(),
                            new_method.clone(),
                        ]
                    })
                    .collect::<Vec<_>>();
                enforcer
                    .remove_filtered_policy(1, vec![path.clone(), method.clone()])
                    .await
                    .unwrap();
                enforcer.add_policies(policies).await.unwrap();
                // enforcer.save_policy().await.unwrap();
            }
        } else {
            // status 改变了
            if new_status {
                //启用
                let policies = ane_system::dao::Resource::fetch_roles(state.db(), id)
                    .await?
                    .into_iter()
                    .filter(|f| f.status)
                    .map(|f| {
                        vec![
                            format!("user_{}", f.id),
                            new_path.clone(),
                            new_method.clone(),
                        ]
                    })
                    .collect::<Vec<_>>();
                let mut enforcer = enforcer.lock().await;
                enforcer.add_policies(policies).await.unwrap();
                // enforcer.save_policy().await.unwrap();
            } else {
                //停用
                let mut enforcer = enforcer.lock().await;
                let path = resource.path;
                let method = resource.method;
                enforcer
                    .remove_filtered_policy(1, vec![path.clone(), method.clone()])
                    .await
                    .unwrap();
                // enforcer.save_policy().await.unwrap();
            }
        }
    }
    res
}

/// 删除resource信息，更新casbin
pub async fn delete(
    Extension(state): Extension<Arc<AppState>>,
    Path(id): Path<i32>,
) -> AppResult<impl IntoResponse> {
    let resource = ane_system::dao::Resource::fetch_one(state.db(), id)
        .await?
        .ok_or(AppError::NotFount)?;
    let txn = state.db().begin().await?;
    let res = ane_system::dao::Resource::delete(&txn, id)
        .await
        .map_err(Into::into)
        .map(|_| "Ok");
    txn.commit().await?;
    {
        let mut enforcer = state.enforcer().lock().await;
        enforcer
            .remove_filtered_policy(1, vec![resource.path, resource.method])
            .await
            .unwrap();
        // enforcer.save_policy().await.unwrap();
    }
    res
}
